package com.welica.wat.impl;

import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.welica.wat.config.RedisLockConfig;
import com.welica.wat.stragety.DistributeLockStrategy;

/**
 * @ClassName: RedisLock
 * @Description: redis实现分布式锁的实现类 type可扩展 zookeeper、cousel、etcd...
 * @author weiyongpeng
 * @date 2024年4月20日 下午5:14:19
 */
public class RedisLock implements DistributeLockStrategy {

	private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
	
	private static final String REDISSON_PERFIX = "redis://";

	private RedissonClient[] redissonClients;

	private RedisLockConfig lockConfig;

	public RedisLock(RedisLockConfig lockConfig) {
		super();
		// 不设置 默认是单机模式
		if (lockConfig.getHost() == null) {
			lockConfig.setHost(new String[] { "127.0.0.1:6379" });
		}
		this.redissonClients = new RedissonClient[lockConfig.getHost().length];
		this.lockConfig = lockConfig;
	}

	/**
	* 描述：初始化配置
	* @Title: init
	* @author weiyongpeng
	* @date  2024年4月20日 下午6:40:23
	 */
	@PostConstruct
	public void init() {
		Config config = new Config();
		if (lockConfig.getMode().equals("single") || lockConfig.getMode().equals("multi")) {
			// 不设置 默认是单机模式
			if (lockConfig.getHost() == null) {
				lockConfig.setHost(new String[] { "127.0.0.1:6379" });
			}
			// 初始化
			redissonClients = new RedissonClient[lockConfig.getHost().length];

			for (int i = 0; i < lockConfig.getHost().length; i++) {
				config.useSingleServer().setAddress(REDISSON_PERFIX + lockConfig.getHost()[i]);
				redissonClients[i] = Redisson.create(config);
			}
		}else if(lockConfig.getMode().equals("cluster")) {
			ClusterServersConfig clusterServers = config.useClusterServers();
			for (int i = 0; i < lockConfig.getHost().length; i++) {
				clusterServers.addNodeAddress(REDISSON_PERFIX + lockConfig.getHost()[i]);
			}
			redissonClients[0] = Redisson.create(config);
		}else if(lockConfig.getMode().equals("sentinel")) {
			SentinelServersConfig sentinelServers = config.useSentinelServers();
			for (int i = 0; i < lockConfig.getHost().length; i++) {
				sentinelServers.addSentinelAddress(REDISSON_PERFIX + lockConfig.getHost()[i]);
			}
			redissonClients[0] = Redisson.create(config);
		}

	}

	/**
	 * 加锁
	 * @param lockName
	 * @param ttl
	 * @param isSpin
	 */
	@Override
	public boolean lock(String lockName, int ttl, boolean isSpin) {
		// 红锁
		boolean isLock = true;
		RLock lock = getRLock(lockName);
		if (isSpin) { // 自旋
			if (ttl == -1) { // 未设置leasetime为开门狗机制，自动续期锁的时效时间，防止因业务逻辑执行时间超长导致的锁失效
				lock.lock();
			} else {
				lock.lock(ttl, TimeUnit.SECONDS);
			}
		} else {
			try {
				if (ttl == -1) {
					isLock = lock.tryLock(lockConfig.getWait(), TimeUnit.SECONDS);
				} else {
					isLock = lock.tryLock(lockConfig.getWait(), ttl, TimeUnit.SECONDS);
				}
			} catch (InterruptedException e) {
				log.error("异常信息：[{}]", e);
				isLock = false;
			}
		}
		if (isLock) {
			log.info("redis加锁成功，锁名称：{}" , lockName);
		}
		return isLock;
	}

	/**
	 * 释放锁
	 * @param lockName
	 */
	@Override
	public boolean unLock(String lockName) {
		try {
			RLock lock = getRLock(lockName);
			lock.unlock();
			log.info("redis释放锁成功，锁名称：{}" , lockName);
			return true;
		} catch (Exception e) {
			log.error("异常信息：[{}]", e);
			log.info("redis释放锁出错，锁名称：{}" , lockName);
			return false;
		}
	}

	/**
	 * 是否存在锁
	 * @param lockName
	 * @return
	 */
	@Override
	public boolean isLocked(String lockName) {
		RLock lock = getRLock(lockName);
		return lock.isLocked();
	}

	private RLock getRLock(String lockName) {
		RLock[] rLocks = new RLock[redissonClients.length];
		for (int i = 0; i < redissonClients.length; i++) {
			rLocks[i] = redissonClients[i].getLock(lockName);
		}
		if (rLocks.length > 2) {
			return new RedissonRedLock(rLocks);
		} else {
			if (redissonClients.length == 0)
				return null;
			return redissonClients[0].getLock(lockName);
		}
	}
}
